package com.kwm;

import java.util.*;



public class demo2 {

    // 一个题目的数据结构
    public static class Problem {
        public final char op; // '+' or '-'
        public final int a;
        public final int b;
        public final int result;

        public Problem(char op, int a, int b, int result) {
            this.op = op;
            this.a = a;
            this.b = b;
            this.result = result;
        }

        @Override
        public String toString() {
            return String.format("%d %c %d = %d", a, op, b, result);
        }

        // 显示用于出题的文本 (隐藏答案)
        public String displayText() {
            return String.format("%d %c %d = __", a, op, b);
        }
    }

    private final Random rnd;

    public demo2() {
        // 固定 seed 方便示例可重复；生产可去掉 seed 或传入不同 seed。
        this.rnd = new Random(42);
    }


    // 加法题生成模块


    /**
     * 生成指定数量的唯一加法题，保证 a + b <= maxSum。
     * 对换律 (a+b 和 b+a) 被视为相同题（使用 canonical (min,max) 去重）。
     */
    public List<Problem> generateAdditionProblems(int count, int maxSum) {
        if (count < 0) throw new IllegalArgumentException("count must be non-negative");
        Set<String> seen = new HashSet<>(); // 存 canonical key "min,max"
        List<Problem> out = new ArrayList<>(count);
        int attempts = 0, maxAttempts = count * 2000 + 1000;
        while (out.size() < count && attempts < maxAttempts) {
            int a = rnd.nextInt(maxSum + 1); // 0..maxSum
            int b = rnd.nextInt(maxSum + 1 - a); // 保证 sum <= maxSum
            int keyA = Math.min(a, b);
            int keyB = Math.max(a, b);
            String key = keyA + "," + keyB;
            if (seen.contains(key)) {
                attempts++;
                continue;
            }
            seen.add(key);
            out.add(new Problem('+', a, b, a + b));
            attempts++;
        }
        if (out.size() < count) {
            throw new RuntimeException("无法在约束内生成足够的唯一加法题");
        }
        return out;
    }


    // 减法题生成模块


    /**
     * 生成指定数量的唯一减法题，保证 a >= b，结果非负且都在 0..maxValue。
     * 减法视为有序 (a,b) 去重。
     */
    public List<Problem> generateSubtractionProblems(int count, int maxValue) {
        if (count < 0) throw new IllegalArgumentException("count must be non-negative");
        Set<String> seen = new HashSet<>(); // 存 "a,b"
        List<Problem> out = new ArrayList<>(count);
        int attempts = 0, maxAttempts = count * 2000 + 1000;
        while (out.size() < count && attempts < maxAttempts) {
            int a = rnd.nextInt(maxValue + 1); // 0..maxValue
            int b = rnd.nextInt(a + 1); // 0..a, 确保 b<=a
            String key = a + "," + b;
            if (seen.contains(key)) {
                attempts++;
                continue;
            }
            seen.add(key);
            out.add(new Problem('-', a, b, a - b));
            attempts++;
        }
        if (out.size() < count) {
            throw new RuntimeException("无法在约束内生成足够的唯一减法题");
        }
        return out;
    }


    // 混合生成控制器


    /**
     * 生成 total 道混合题，按 addRatio 控制加法比例（0..1）。
     * 合并后再做全局唯一性检查（'+' canonical 与 '-' 有序不冲突）。
     */
    public List<Problem> generateMixedProblems(int total, int maxValue, double addRatio) {
        if (total < 0) throw new IllegalArgumentException("total must be non-negative");
        if (addRatio < 0 || addRatio > 1) throw new IllegalArgumentException("addRatio must be 0..1");

        int wantAdd = (int) Math.round(total * addRatio);
        int wantSub = total - wantAdd;

        List<Problem> adds = generateAdditionProblems(wantAdd, maxValue);
        List<Problem> subs = generateSubtractionProblems(wantSub, maxValue);

        // 合并并防止表达式重复（'+' 使用 canonical (min,max)， '-' 使用 (a,b)）
        Set<String> exprSet = new HashSet<>();
        List<Problem> combined = new ArrayList<>();

        for (Problem p : adds) {
            String key = "+" + Math.min(p.a, p.b) + "," + Math.max(p.a, p.b);
            if (exprSet.add(key)) combined.add(p);
        }
        for (Problem p : subs) {
            String key = "-" + p.a + "," + p.b;
            if (exprSet.add(key)) combined.add(p);
        }

        // 如果合并后数量不足，继续补生成直到达到 total（随机选择加或减生成并检测冲突）
        int attempts = 0;
        while (combined.size() < total && attempts < 5000) {
            boolean genAdd = rnd.nextBoolean();
            Problem extra = genAdd ? generateAdditionProblems(1, maxValue).get(0)
                    : generateSubtractionProblems(1, maxValue).get(0);
            String key = (extra.op == '+' ? "+" + Math.min(extra.a, extra.b) + "," + Math.max(extra.a, extra.b)
                    : "-" + extra.a + "," + extra.b);
            if (exprSet.add(key)) combined.add(extra);
            attempts++;
        }

        if (combined.size() < total) throw new RuntimeException("无法生成足够的唯一混合题");

        // 随机打乱顺序并截取前 total
        Collections.shuffle(combined, rnd);
        return combined.subList(0, total);
    }


    // 显示模块


    /**
     * 将题目按网格格式化输出（cols 列）。
     */
    public String formatProblemsGrid(List<Problem> problems, int cols) {
        StringBuilder sb = new StringBuilder();
        int i = 0;
        for (Problem p : problems) {
            sb.append(String.format("%-16s", p.displayText()));
            i++;
            if (i % cols == 0) sb.append(System.lineSeparator());
        }
        if (i % cols != 0) sb.append(System.lineSeparator());
        return sb.toString();
    }


    // 校验模块（单题验证）


    /**
     * 验证题目是否合法并是否算术正确。
     * 返回 (boolean valid, String message)
     */
    public static Map.Entry<Boolean, String> validateProblem(Object obj, int maxValue) {
        if (obj == null) return new AbstractMap.SimpleEntry<>(false, "问题为空 (null)");
        if (!(obj instanceof Problem)) return new AbstractMap.SimpleEntry<>(false, "对象不是 Problem 类型");
        Problem p = (Problem) obj;
        if (p.op != '+' && p.op != '-') return new AbstractMap.SimpleEntry<>(false, "无效操作符: " + p.op);
        if (p.a < 0 || p.b < 0 || p.a > maxValue || p.b > maxValue)
            return new AbstractMap.SimpleEntry<>(false, "操作数超出范围 0.." + maxValue);
        if (p.op == '+') {
            if (p.a + p.b != p.result) return new AbstractMap.SimpleEntry<>(false, "加法结果错误");
            if (p.result > maxValue) return new AbstractMap.SimpleEntry<>(false, "加法结果超过最大值 " + maxValue);
        } else {
            if (p.a < p.b) return new AbstractMap.SimpleEntry<>(false, "减法非法: 被减数小于减数");
            if (p.a - p.b != p.result) return new AbstractMap.SimpleEntry<>(false, "减法结果错误");
        }
        return new AbstractMap.SimpleEntry<>(true, "OK");
    }


    // 测试模块

    public void runTests() {
        System.out.println("=== 运行基本测试集 ===");

        // 正确数据
        List<Object> correct = Arrays.asList(
                new Problem('+', 12, 5, 17),
                new Problem('-', 20, 5, 15),
                new Problem('+', 0, 0, 0),
                new Problem('-', 100, 0, 100)
        );

        // 错误数据（结果错或不该有该操作符）
        List<Object> incorrect = Arrays.asList(
                new Problem('+', 7, 3, 11),  // 错误结果
                new Problem('-', 5, 3, 1),   // 错误结果
                new Problem('*', 2, 2, 4)    // 非法操作符 (注意：char '*' 也被接收，但判定为非法)
        );

        // 非法数据（类型或范围或格式错误）
        List<Object> illegal = Arrays.asList(
                null,                       // 空对象
                "not a problem",            // 非 Problem 类型
                new Problem('+', -1, 2, 1), // 负数操作数
                new Problem('+', 60, 50, 110) // 和超过 100
        );

        System.out.println("-- 正确数据 --");
        for (Object o : correct) {
            Map.Entry<Boolean, String> res = validateProblem(o, 100);
            System.out.printf("Input: %s -> Valid: %s, Msg: %s%n", o, res.getKey(), res.getValue());
        }
        System.out.println();

        System.out.println("-- 错误数据 --");
        for (Object o : incorrect) {
            Map.Entry<Boolean, String> res = validateProblem(o, 100);
            System.out.printf("Input: %s -> Valid: %s, Msg: %s%n", o, res.getKey(), res.getValue());
        }
        System.out.println();

        System.out.println("-- 非法数据 --");
        for (Object o : illegal) {
            Map.Entry<Boolean, String> res = validateProblem(o, 100);
            System.out.printf("Input: %s -> Valid: %s, Msg: %s%n", String.valueOf(o), res.getKey(), res.getValue());
        }
        System.out.println("=== 测试结束 ===");
    }

    // -------------------------
    // Main demo
    // -------------------------
    public static void main(String[] args) {
        demo2 gen = new demo2();

        // 生成 50 道混合题
        List<Problem> problems = gen.generateMixedProblems(50, 100, 0.5);

        // 格式化显示（5 列）
        System.out.println("生成50道算数题目:\n");
        System.out.println(gen.formatProblemsGrid(problems, 5));

        // 如果需要答案（仅用于验证），打印详细带答案列表
        System.out.println("带有正确答案算式的题目:");
        int idx = 1;
        for (Problem p : problems) {
            System.out.printf("%2d. %s = %d%n", idx++, (p.a + " " + p.op + " " + p.b), p.result);
        }
        System.out.println();

        // 运行测试集
        gen.runTests();
    }
}

